/*
 * Copyright 2008 Lucifer Lu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

using SilverChart.Configuration;
using SilverChart.Core.Log;

namespace SilverChart.Components {
    /// <summary>
    /// Description of XAxisComponent.
    /// </summary>
    public class XAxisComponent : CartesianCoordinateAxis {
        private static Logger logger = Logger.GetLogger(typeof(XAxisComponent).FullName);

        private Int32 LabelOffset;

        private List<TextBlock> textLabels;

        private Path xAxisLine;
        private Path minorTickLines;
        private GeometryGroup minroTickGroup;
        private Path majorTickLines;
        private GeometryGroup majorTickGroup;

        public XAxisComponent(XAxis config)
            : base(config) {
            //Background = BrushHelper.GetSolidColorBrush("Red");
            TickWidth = 0;

            MaxValue = 0;

            ValuePerTick = 1;
            if (config.LabelStartFromZero) {
                LabelOffset = 0;
            }
            else {
                LabelOffset = 1;
            }

            MinValue = 0;
            textLabels = new List<TextBlock>();

            xAxisLine = AxisConfig.AxisLine;
            Children.Add(xAxisLine);

            minorTickLines = AxisConfig.MinorTickLine;
            minroTickGroup = new GeometryGroup();
            minorTickLines.Data = minroTickGroup;
            Children.Add(minorTickLines);

            majorTickLines = AxisConfig.MajorTickLine;
            majorTickGroup = new GeometryGroup();
            majorTickLines.Data = majorTickGroup;
            Children.Add(majorTickLines);
        }

        public override void InitializeWidgets() {
            ElementsCount();
            addLabels();
        }

        private void addLabels() {
            for (Int32 i = (Int32)MinValue; i <= (Int32)MaxValue; i += (Int32)ValuePerTick) {
                if ((i != 0 || LabelOffset == 0) && (i + LabelOffset) % AxisConfig.Step == 0) {
                    TextBlock tickLabel = AxisConfig.LabelText;
                    Int32 labelIndex = (i - LabelOffset) / AxisConfig.Step - (Int32)MinValue;
                    if (labelIndex < AxisConfig.Labels.Count) {
                        tickLabel.Text = AxisConfig.Labels[labelIndex];
                        logger.Debug("XAxis Label[" + i + "]:" + AxisConfig.Labels[labelIndex]);
                    }
                    else {
                        tickLabel.Text = i.ToString();
                    }
                    Children.Add(tickLabel);
                    textLabels.Add(tickLabel);
                }
            }
        }

        public Double TickWidth { get; private set; }

        public override Double MappingValueToAxis(Double value) {
            return value * TickWidth;
        }

        private void ElementsCount() {
            foreach (Element element in Datas) {
                if (element.Values.Count > (Int32)MaxValue) {
                    MaxValue = element.Values.Count;
                }
            }

            MaxValue = MaxValue - 1 + LabelOffset;

            if ((AxisConfig.Labels.Count - 1 + LabelOffset) * AxisConfig.Step > (Int32)MaxValue) {
                MaxValue = (AxisConfig.Labels.Count - 1 + LabelOffset) * AxisConfig.Step;
            }

            logger.Info("TickCount:" + ((Int32)MaxValue).ToString());
        }

        protected override Size MeasureOverride(Size availableSize) {
            Double height = AxisConfig.Thickness;
            height += Math.Max(AxisConfig.MinorTickHeight, AxisConfig.MajorTickHeight);

            TickWidth = availableSize.Width / ((Int32)MaxValue + 1);

            Double maxLabelHeigth = 0;

            Size labelSize = new Size(TickWidth * AxisConfig.Step, availableSize.Height);
            foreach (TextBlock tb in textLabels) {
                tb.Measure(labelSize);
                if (tb.DesiredSize.Height > maxLabelHeigth) {
                    maxLabelHeigth = tb.DesiredSize.Height;
                }

            }

            height += maxLabelHeigth;
            return new Size(availableSize.Width, height);
        }

        protected override Size ArrangeOverride(Size finalSize) {
            Double xAxisY = AxisConfig.Thickness / 2;

            LineGeometry axisLg = new LineGeometry();
            axisLg.StartPoint = new Point(0, xAxisY);
            axisLg.EndPoint = new Point(finalSize.Width, xAxisY);
            xAxisLine.Data = axisLg;

            Double axisLineUnder = xAxisY + AxisConfig.Thickness / 2;
            Double labelTop = axisLineUnder +
                Math.Max(AxisConfig.MajorTickHeight, AxisConfig.MinorTickHeight);
            Int32 labelIndex = 0;
            Int32 tickCount = 0;
            for (Int32 i = (Int32)MinValue; i <= (Int32)MaxValue; i += (Int32)ValuePerTick) {
                Double tickLeft = MappingValueToAxis(i);

                if (MajorTick(tickCount)) {
                    LineGeometry tickLg = new LineGeometry();
                    tickLg.StartPoint = new Point(tickLeft, axisLineUnder);
                    tickLg.EndPoint = new Point(tickLeft, axisLineUnder + AxisConfig.MajorTickHeight);
                    majorTickGroup.Children.Add(tickLg);

                    TextBlock tickLabel = textLabels[labelIndex];
                    Rect labelOutBorder = new Rect(tickLeft - tickLabel.DesiredSize.Width / 2, labelTop,
                        tickLabel.DesiredSize.Width, tickLabel.DesiredSize.Height);
                    tickLabel.Arrange(labelOutBorder);
                    labelIndex++;
                }
                else {
                    if (tickCount != 0) {
                        LineGeometry tickLg = new LineGeometry();
                        tickLg.StartPoint = new Point(tickLeft, axisLineUnder);
                        tickLg.EndPoint = new Point(tickLeft, axisLineUnder + AxisConfig.MinorTickHeight);
                        minroTickGroup.Children.Add(tickLg);
                    }
                }
                tickCount++;
            }

            return finalSize;
        }

        public override string WidgetName {
            get {
                return "X Axis Component";
            }
        }

        public override DockType Dock {
            get {
                return DockType.Bottom;
            }
        }

        public override Boolean MajorTick(int tickCount) {
            return (tickCount!=0 || LabelOffset == 0) && (tickCount + LabelOffset) % AxisConfig.Step == 0;
        }
    }
}
